package db;

import encrypt.AesTool;

import java.io.IOException;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
/**
 * @author: wzq
 * @date: 14-6-17
 * description: change it at File | setting | File and code templates | include | file header
 */
public class BeanAssist {

    /**
     * 加密对象,<b>会修改原对象</b>
     *
     */
    public static void encryptObj(Object obj) {
        encryptOrDecryptObject(obj, true);
    }
    /**
     *  解密对象,<b>会修改原对象</b>
     */
    public static void decryptObj(Object obj) {
        encryptOrDecryptObject(obj, false);
    }

    /**
     * 找到sql语句中的 加密字段的值，使其加密
     */
    public static String encryptSql(String sql, Class<?> clazz) {
        Field[] fields = clazz.getDeclaredFields();
        ArrayList<String> cryptFieldsColumnName = new ArrayList<String>();
        for(Field field : fields) {
            if(isFieldInvolveCrypt(field)) {
                CryptField cf = field.getAnnotation(CryptField.class);
                String colName = cf.colName();
                cryptFieldsColumnName.add(colName);
            }
        }

        if(cryptFieldsColumnName.size() == 0) {
            return sql;
        }

        // 在 sql 中查询 name = 'myName' 的字段
        // regExp =  空白+数据库字段名+空白{0,1}+ 等号 + ' + 值 + '
        // \\s  %s  \\s{0,}  =  \\'  \\w{1,}  \\'
        String strPtn = "\\s%s\\s{0,}=\\'\\w{1,}\\'";
        String cryptSql = new String(sql);
        for(String colName : cryptFieldsColumnName) {
            if(cryptSql.indexOf(colName) == -1) {
                continue;
            }
            String aStrPtn = String.format(strPtn, colName);
            Pattern pattern = Pattern.compile(aStrPtn);
            Matcher matcher = pattern.matcher(cryptSql);
            while(matcher.find()) {
                String s = matcher.group();

                //获取引号内的值，加密
                String v = s.substring(s.indexOf("'") + 1, s.length() - 1);
                String cryptV = AesTool.getInstance().encryptString(v);

                // 组装替换的串
                StringBuilder sb = new StringBuilder();
                sb.append(s.substring(0, s.indexOf("'") + 1));
                sb.append(cryptV);
                sb.append("'");

                // 进行替换
                cryptSql = cryptSql.replace(s, sb.toString());
            }
        }

        return cryptSql;
    }

    /**
     * 将一个对象进行加/解密,修改原对象
     *
     * @param t 需要加解密的对象
     * @param isEncrypt true表示加密操作，false表示解密操作
     */
    private static void encryptOrDecryptObject(Object t, boolean isEncrypt) {
        Class<?> clazz = t.getClass();

        Field[] fields = clazz.getDeclaredFields();
        for(Field field : fields) {
            if(!isFieldInvolveCrypt(field)) {
                continue;
            }

            // 获取field的值，加密，并设置
            String fieldName = field.getName();
            Method getter = null;
            Object fieldValue = null;
            try {
                getter = clazz.getMethod(retrieveGetter(fieldName));
                fieldValue = getter.invoke(t);
                // String 类型
                if(fieldValue instanceof String) {
                    String postValue = null;
                    if(isEncrypt) {
                        postValue = AesTool.getInstance().encryptString((String) fieldValue);
                    } else {
                        postValue = AesTool.getInstance().decryptString((String) fieldValue);
                    }
                    Method setter = clazz.getDeclaredMethod(retrieveSetter(fieldName), String.class);
                    setter.invoke(t, postValue);
                }
                // byte[] 类型
                else if(fieldValue instanceof byte[]) {
                    byte[] postValue = null;
                    if(isEncrypt) {
                        postValue = AesTool.getInstance().encryptBytes((byte[]) fieldValue);
                    } else {
                        postValue = AesTool.getInstance().decryptBytes((byte[]) fieldValue);
                    }
                    Method setter = clazz.getDeclaredMethod(retrieveSetter(fieldName), byte[].class);
                    setter.invoke(t, postValue);
                }

                // 不支持的类型
                else {
                    String expInfo = "数据类型 " + fieldValue.getClass().getName() + " 不支持 加密处理";
                    throw new UnsupportedOperationException(expInfo);
                }
            } catch(NoSuchMethodException e) {
                e.printStackTrace();
            } catch(IllegalAccessException e) {
                e.printStackTrace();
            } catch(InvocationTargetException e) {
                e.printStackTrace();
            } catch(IOException e) {
                e.printStackTrace();
            }
        }
    }

    /**
     * 首字母大写
     */
    private static String firstLetterCap(String old) {
        String firstCap = old.substring(0, 1).toUpperCase() + old.substring(1);
        return firstCap;
    }

    /**
     * 获取getter方法名
     */
    private static String retrieveGetter(String fieldName) {
        return "get" + firstLetterCap(fieldName);
    }
    /**
     * 获取setter方法名
     */
    private static String retrieveSetter(String fieldName) {
        return "set" + firstLetterCap(fieldName);
    }

    /**
     * 判断一个field是否有Annotation CryptField
     */
    private static boolean isFieldInvolveCrypt(Field field) {
        return field.isAnnotationPresent(CryptField.class);
    }
}
